import unittest
import warnings
import uuid
import logging
import time
from selenium import webdriver

import suds

from Autotests.Cloud.executor import ActionExecutor
from Build.common.CommonTypes import BuildInfo
from Reporting import DatabaseReporter
from Autotests.WebDriver import DRIVER
from Config.commonvars import isDatabaseReportingNeeded
from Config.RunConfig import RunConfig, Options
from os.path import exists
import os
from os.path import join

from Autotests.LocalMachine.Pages.AirManager.LoginPage import WebLoginPage


class BaseTestCase(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        super(BaseTestCase, self).__init__(*args, **kwargs)
        self._errorsBeforeTest = 0
        self._failsBeforeTest = 0
        self.executor = ActionExecutor()
        self.logger = logging.getLogger()
        self.warnings = warnings
        self._isDbReportNeeded = True

    def run(self, result=None):
        self._errorsBeforeTest = len(result.errors)
        self._failsBeforeTest = len(result.failures)
        return super(BaseTestCase, self).run(result)

    def currentResult(self):
        errors = len(self._resultForDoCleanups.errors) - self._errorsBeforeTest
        fails = len(self._resultForDoCleanups.failures) - self._failsBeforeTest
        return errors, fails

    def takeScreenshot(self):
        pass

    def setUp(self):
        if isDatabaseReportingNeeded:
            self._startTime = DatabaseReporter.getDbTime()
        self.warnings.showwarning

    def tearDownImpl(self):
        pass

    def postDbReport(self):
        resultId = DatabaseReporter.reportResult(self.caseName(), self._startTime, DatabaseReporter.getDbTime(),
                                                 self.getBuildInfo(), *self.currentResult())
        DatabaseReporter.reportPerformance(self.getPerformanceResult())
        return resultId

    def tearDown(self):
        self.takeScreenshot()
        self.tearDownImpl()
        if isDatabaseReportingNeeded:
            self.postDbReport()

    def getPerformanceResult(self):
        return None

    def caseName(self):
        return self.id().split('.')[-1].split('_')[-1]

    def getBuildInfo(self):
        return BuildInfo()

    def timeoutAssertTrue(self, predicate, msg='', timeoutSec=10, tickIntervalSec=0.5):
        mustend = time.time() + timeoutSec
        while time.time() < mustend and not predicate():
            time.sleep(tickIntervalSec)
        self.assertTrue(predicate(), msg)


class BaseSOAPTestCase(BaseTestCase):
    def assertIsServiceSequence(self, sequence, cls):
        """
            In suds there is a bug:
                when Clients call returns empty sequence, it returns
                suds.sax.text.Text object what is equal with empty
                string but not response structure
            This method skips assertions if sequence is suds.sax.text.Text
                object."""
        if isinstance(sequence, suds.sax.text.Text):
            warnings.warn("sequence is a text")
        else:
            self.assertIsInstance(sequence, cls)

    def sudsSequenceLen(self, sequence, sequenceField = None):
        if isinstance(sequence, suds.sax.text.Text):
            return 0
        elif sequenceField:
            return len(getattr(sequence, sequenceField))
        return len(sequence)

    def assertInSudsSequence(self, item, sequence):
        item = str(item)
        for it in sequence:
            if str(it) == item:
                return
        self.fail("sequence not contain item")


class BaseManservSOAPTestCase(BaseSOAPTestCase):
    def __init__(self, *args, **kwargs):
        super(BaseManservSOAPTestCase, self).__init__(*args, **kwargs)
        self.manserv = self.executor._partnerManageProvider.manserv  # TODO: make normal mechanism for common ManSvc

    def getBuildInfo(self):
        return BuildInfo(version=self.executor.getServerInfo().version)

    def postDbReport(self):
        resultId = super(BaseManservSOAPTestCase, self).postDbReport()
        DatabaseReporter.reportSoapInfoForResult(resultId, self.manserv.getProtocolVersion())


class BaseWebTestCase(BaseTestCase):
    def __init__(self, *args, **kwargs):
        super(BaseWebTestCase, self).__init__(*args, **kwargs)

    def takeScreenshot(self):
        errors, fails = self.currentResult()
        if errors + fails > 0 and RunConfig().getBoolean(Options.AirBackup.takeScreenshot):
            screenDir = join(RunConfig().get(Options.AirBackup.workingDir), DRIVER().browser())
            if not exists(screenDir):
                os.makedirs(screenDir)
            DRIVER().save_screenshot(join(screenDir, "%s.png" % self.caseName()))

    def tearDown(self):
        try:
            super(BaseWebTestCase, self).tearDown()
        finally:
            DRIVER().quit()

    def currentUrl(self):
        return DRIVER().current_url

    def currentWinHandler(self):
        return DRIVER().current_window_handle


class AirClientTestCase(BaseWebTestCase):
    def __init__(self, *args, **kwargs):
        super(AirClientTestCase, self).__init__(*args, **kwargs)

    def setUp(self):
        super(AirClientTestCase, self).setUp()
        DRIVER(makeNew=True).get(RunConfig().get(Options.AirBackup.clientAddress))


class AirManagerTestCase(BaseWebTestCase):
    def __init__(self, *args, **kwargs):
        super(AirManagerTestCase, self).__init__(*args, **kwargs)

    def setUp(self):
        super(AirManagerTestCase, self).setUp()
        # profile = webdriver.FirefoxProfile()
        # profile.native_events_enabled = False
        # DRIVER(makeNew=True, firefox_profile=profile).get(RunConfig().get(Options.AirBackup.managerAddress))
        DRIVER(makeNew=True).get(RunConfig().get(Options.AirBackup.managerAddress))
        WebLoginPage().performLogin("andrei.tsishenka@iaso.com", "secret123")

def PartnerAndAccountAutoCreation(SoapTestCls):
    class Decorator(SoapTestCls):
        def __init__(self, *args, **kwargs):
            SoapTestCls.__init__(self, *args, **kwargs)
            self._partnerForCaseName = str(uuid.uuid4())
            self._accountForCaseName = str(uuid.uuid4())
            self._accountForCasePassword = str(uuid.uuid4())
            self._parentPartner = RunConfig().get(Options.Soap.defaultPartner)
            self._partnerLevel = 'Distributor'

        def _makeAttr(self, attr, maker, *args, **kwargs):
            if not hasattr(self, attr):
                setattr(self, attr, maker(*args, **kwargs))
            return getattr(self, attr)

        def getPartnerForCaseInfo(self):
            return self._makeAttr("_partnerForCaseInfo",
                                  self.executor.getPartnerInfo, self._partnerForCaseName)

        def getAccountForCaseInfo(self):
            return self._makeAttr("_accountForCaseInfo", self.executor.getAccountInfo,
                                  self._accountForCaseName, self._accountForCasePassword)

        def removePartnerForCase(self):
            try:
                self.executor.removePartner(self._partnerForCaseName, True)
            except Exception:
                pass

        def tearDown(self, *args, **kwargs):
            self.removePartnerForCase()
            super(Decorator, self).tearDown(*args, **kwargs)

        def setUp(self, *args, **kwargs):
            self.removePartnerForCase()
            self.executor.addPartner(parentInfo = self.executor.getPartnerInfo(self._parentPartner),
                                     name = self._partnerForCaseName,
                                     level = self._partnerLevel)
            self.executor.addAccount(self.getPartnerForCaseInfo(), self._accountForCaseName,
                                     self._accountForCasePassword)
            SoapTestCls.setUp(self, *args, **kwargs)

    return Decorator
